package org.sca.middle.cmpe.db;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.sca.middle.cmpe.dto.AuthType;
import org.sca.middle.cmpe.dto.DataTransfer;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

/**
 * Modified 3/28/2014: Added authTypesKey as ancestor to all authType entities.
 * @author rik
 * @author Kevin Carlen
 * @version 1.1
 */
public class AuthTypeDAO implements DatastoreKind {

	/** The Google Datastore service */
	DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

    /** The ancestor key for AuthType */
    Key authTypesKey = KeyFactory.createKey(AUTHTYPES_KIND, AUTHTYPES_KEY);

    public static class LocalCacheImpl extends LocalCacheAbImpl {

        private static final LocalCacheImpl _instance = new LocalCacheImpl();

        public static LocalCacheImpl getInstance() {
            return _instance;
        }
    }
    /** The cache for AuthTypes */
    private static final LocalCacheImpl localCache = (LocalCacheImpl) LocalCacheImpl.getInstance();

    /**
     * Searches for AuthType in the cache. If not present then retrieves from Datastore.
     * @param key the entity key to search for
     * @return the AuthType
     * @throws NotFoundException if the key does not exist in the cache or in the Datastore
     */
    public AuthType getAuthType(Key key) throws NotFoundException {
        AuthType authType = (AuthType) localCache.getValue(key.getId());
        if (authType == null) {
            Entity authEntity;
            try {
                authEntity = datastore.get(key);
            } catch (EntityNotFoundException ex) {
                Logger.getLogger(AuthTypeDAO.class.getName()).log(Level.SEVERE, null, ex);
                throw new NotFoundException(AUTHTYPE_KIND, key.getId());
            }
            authType = DataTransfer.convertAuthType(authEntity);
            localCache.put(key.getId(), authType);
        }
        return authType;
    }

    /**
     * Searches for AuthType in the cache. If not present then retrieves from Datastore.
     * @param authTypeId the entity key id to search for
     * @return the AuthType
     * @throws NotFoundException if the key id does not exist in the cache or in the Datastore
     */
    public AuthType getAuthType(long authTypeId) throws NotFoundException {
        AuthType authType = (AuthType) localCache.getValue(new Long(authTypeId));
        if (authType == null) {
            Key key = KeyFactory.createKey(authTypesKey, AUTHTYPE_KIND, authTypeId);
            Entity authEntity;
            try {
                authEntity = datastore.get(key);
            } catch (EntityNotFoundException ex) {
                Logger.getLogger(AuthTypeDAO.class.getName()).log(Level.SEVERE, null, ex);
                throw new NotFoundException(AUTHTYPE_KIND, authTypeId);
            }
            authType = DataTransfer.convertAuthType(authEntity);
            localCache.put(Long.valueOf(authTypeId), authType);
        }
        return authType;
    }

    /**
     * @param code
     * @return
     */
    public Key getAuthTypeKeyByCode(String code) {
        Query.Filter codeFilter = new Query.FilterPredicate("code", Query.FilterOperator.EQUAL, code);
        Query query = new Query(AUTHTYPE_KIND, authTypesKey).setFilter(codeFilter);
        List<Entity> atList = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
        if (!atList.isEmpty()) {
            return atList.get(0).getKey();
        }
        return null;
    }

    /**
     * @param code
     * @return
     */
    public AuthType getAuthTypeByCode(String code) {
        AuthType at = (AuthType) localCache.getValue(code);
        if (at == null) {
            Query.Filter codeFilter = new Query.FilterPredicate("code", Query.FilterOperator.EQUAL, code);
            Query query = new Query(AUTHTYPE_KIND, authTypesKey).setFilter(codeFilter);
            List<Entity> atList = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
            if (!atList.isEmpty()) {
                at = DataTransfer.convertAuthType(atList.get(0));
            }
            localCache.put(code, at);
        }
        return at;
    }

    public List<AuthType> getAuthType() {
        @SuppressWarnings("unchecked")
		List<AuthType> atList = (List<AuthType>) localCache.getValue("atlist");
        if (atList == null || atList.isEmpty()) {
            Query query = new Query(AUTHTYPE_KIND, authTypesKey).addSort("orderValue");
            List<Entity> entityList = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
            atList = new ArrayList<AuthType>(entityList.size());
            for (Entity entity : entityList) {
                AuthType newAt = DataTransfer.convertAuthType(entity);
                atList.add(newAt);
            }
        }
        localCache.put("atlist", atList);
        return atList;
    }

    /**
     * Save AuthType to Datastore.
     * @param authType {@link AuthType} to save
     * @return the key of the AuthType entity
     */
    public Key saveAuthType(AuthType authType) {
        localCache.clear();

        Entity at = null;
        if (authType.getAuthTypeId() != null && authType.getAuthTypeId() > 0) {
            Key authTypeKey = KeyFactory.createKey(authTypesKey, AUTHTYPE_KIND, authType.getAuthTypeId());
            try {
                at = datastore.get(authTypeKey);
            } catch (EntityNotFoundException ex) {
                Logger.getLogger(AuthTypeDAO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (at == null) {
            at = new Entity(AUTHTYPE_KIND, authTypesKey);
        }
        AuthCategoryDAO authCategoryDAO = new AuthCategoryDAO();
        at.setProperty("code", authType.getCode());
        at.setProperty("description", authType.getDescription());
        at.setProperty("category", authCategoryDAO.getAuthCategoryKey(authType.getCategory())); //TODO Look up and store Key
        at.setProperty("marshal", Boolean.valueOf(authType.isMartial()));
        at.setProperty("orderValue", authType.getOrderValue());

        Key key = datastore.put(at);
        authType.setAuthTypeId(key.getId());

        return key;
    }
}
